package com.example.mapping.manage.execute;

import com.example.mapping.common.exception.MappingBizException;
import com.example.mapping.entity.*;
import com.example.mapping.entity.process.JsonMappingProcessInfo;
import com.example.mapping.entity.visit.JsonMappingVisitInfo;
import com.example.mapping.manage.FlowModelManage;
import com.example.mapping.manage.factory.Factory;
import com.example.mapping.manage.processor.JsonFieldProcessor;
import com.example.mapping.manage.processor.JsonValueProcessor;
import com.example.mapping.manage.visit.Visitor;
import com.example.mapping.utils.JacksonUtils;
import com.example.mapping.vo.FieldMappingVo;
import com.example.mapping.vo.FlowModelVo;
import com.example.mapping.vo.RuleVo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import okhttp3.*;
import org.apache.commons.lang3.StringEscapeUtils;
import org.mapstruct.Mapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 抽象的流程执行器
 *
 * @author baoyoujia
 */
public class AbstractFlowExecutor implements FlowExecutor {

    @Autowired
    private FlowModelManage flowModelManage;

    @Autowired
    private Factory factory;

    @Autowired
    private JsonFieldProcessor jsonFieldProcessor;

    @Autowired
    private JsonValueProcessor jsonValueProcessor;

    @Value("${mapping.expression.req}")
    private String reqMappingExpressionValue;

    @Value("${mapping.expression.resp}")
    private String respMappingExpressionValue;

    @Override
    public String execute(String reqJsonStr) throws Exception {
        MappingExpression reqMappingExpression = JacksonUtils.parseObject(reqMappingExpressionValue, MappingExpression.class);
        MappingExpression respMappingExpression = JacksonUtils.parseObject(respMappingExpressionValue, MappingExpression.class);

        // 请求转换
        String transformJson = reqTransform(reqMappingExpression, reqJsonStr);
        // 请求下游 获取响应
        String respJsonStr = exec("http://localhost:8091/test", transformJson);
        // 转换下游响应
        String respDataJsonStr = respTransform(respMappingExpression, respJsonStr);
        //转换响应值
        String transform = respResultTransform(respMappingExpression, respDataJsonStr);

        return transform;
    }

    /**
     * 转化响应结果
     *
     * @param respMappingExpression 响应结果字段
     * @param respDataJsonStr            响应数据json
     */
    private String respResultTransform(MappingExpression respMappingExpression, String respDataJsonStr) throws JsonProcessingException {
        ObjectNode jsonNodes = new ObjectMapper().readValue(respDataJsonStr, ObjectNode.class);
        if (Objects.isNull(respMappingExpression)) {
            return JacksonUtils.toJSONString(jsonNodes);
        }
        List<JsonMappingSpec> fieldJsonList = Optional.ofNullable(respMappingExpression.getValueJson()).orElse(Lists.newArrayList());
        for (JsonMappingSpec jsonMappingSpec : fieldJsonList) {
            // 访问者信息
            JsonMappingVisitInfo jsonMappingVisitInfo = new JsonMappingVisitInfo();
            jsonMappingVisitInfo.setName(jsonMappingSpec.getMappingAbsolute());
            jsonMappingVisitInfo.setNode(jsonNodes);
            Visitor visitor = factory.getInstance(jsonMappingSpec.getType());
            // 执行访问者
            visitor.visit(jsonMappingVisitInfo);
            // 处理器信息
            JsonMappingProcessInfo jsonMappingProcessInfo = new JsonMappingProcessInfo();
            jsonMappingProcessInfo.setNodes(jsonMappingVisitInfo.getNodes());
            jsonMappingProcessInfo.setJsonMappingSpec(jsonMappingSpec);
            // 字段映射处理器
            jsonValueProcessor.handle(jsonMappingProcessInfo);
        }
        return JacksonUtils.toJSONString(jsonNodes);/*
        for (FieldMappingVo fieldMapping : respResultFieldMappingList) {
            Map<String, ValueMappingVo> valueMappingVoMap = fieldMapping.getValueMappingVoList().stream().collect(Collectors.toMap(ValueMappingVo::getSrcValue, Function.identity(), (x, y) -> x));
            String destField = fieldMapping.getDestField();
            if (jsonNodes.has(destField)) {
                ValueMappingVo valueMappingVo = valueMappingVoMap.get(jsonNodes.get(destField).asText());
                if (Objects.isNull(valueMappingVo)) {
                    continue;
                }
                DestValueTypeEnum destValueTypeEnum = DestValueTypeEnum.valueOf(valueMappingVo.getDestValueType());
                switch (destValueTypeEnum) {
                    case INT:
                        jsonNodes.put(destField, Integer.parseInt(valueMappingVo.getDestValue()));
                        break;
                    case LONG:
                        jsonNodes.put(destField, Long.parseLong(valueMappingVo.getDestValue()));
                        break;
                    case DOUBLE:
                        jsonNodes.put(destField, Double.parseDouble(valueMappingVo.getDestValue()));
                        break;
                    case BOOL:
                        jsonNodes.put(destField, Boolean.parseBoolean(valueMappingVo.getDestValue()));
                        break;
                    default:
                        jsonNodes.put(destField, valueMappingVo.getDestValue());
                }
            }
        }
        return new ObjectMapper().writeValueAsString(jsonNodes);*/
    }

    protected void checkRule(List<FieldMappingVo> reqFieldMappingList, String reqJsonStr) throws Exception {
        if (CollectionUtils.isEmpty(reqFieldMappingList)) {
            return;
        }
        ObjectNode jsonNodes = new ObjectMapper().readValue(reqJsonStr, ObjectNode.class);
        for (FieldMappingVo fieldMapping : reqFieldMappingList) {
            if (CollectionUtils.isEmpty(fieldMapping.getRuleInfoList())) {
                continue;
            }
            String srcField = fieldMapping.getSrcField();
            if (jsonNodes.has(srcField)) {
                JsonNode jsonNode = jsonNodes.get(srcField);
                for (RuleVo ruleVo : fieldMapping.getRuleInfoList()) {
                    boolean matches = Pattern.matches(ruleVo.getPattern(), jsonNode.asText());
                    if (!matches) {
                        throw new MappingBizException(ruleVo.getErrorCode(), ruleVo.getMessage());
                    }
                }
            }
        }
    }

    /**
     * 请求字段转换
     *
     * @param reqMappingExpression 字段映射集合
     * @param reqJsonStr          请求json字符串
     * @return 转换后的json字符串
     * @throws Exception 异常
     */
    public String reqTransform(MappingExpression reqMappingExpression, String reqJsonStr) throws Exception {
        ObjectNode jsonNodes = JacksonUtils.parseObjectNode(reqJsonStr);
        if (Objects.isNull(reqMappingExpression)) {
            return JacksonUtils.toJSONString(jsonNodes);
        }
        List<JsonMappingSpec> fieldJsonList = reqMappingExpression.getFieldJson();
        for (JsonMappingSpec jsonMappingSpec : fieldJsonList) {
            // 访问者信息
            JsonMappingVisitInfo jsonMappingVisitInfo = new JsonMappingVisitInfo();
            jsonMappingVisitInfo.setName(jsonMappingSpec.getName());
            jsonMappingVisitInfo.setNode(jsonNodes);
            // 执行访问者
            Visitor visitor = factory.getInstance(jsonMappingSpec.getType());
            visitor.visit(jsonMappingVisitInfo);
            // 处理器信息
            JsonMappingProcessInfo jsonMappingProcessInfo = new JsonMappingProcessInfo();
            jsonMappingProcessInfo.setNodes(jsonMappingVisitInfo.getNodes());
            jsonMappingProcessInfo.setJsonMappingSpec(jsonMappingSpec);
            // 字段映射处理器
            jsonFieldProcessor.handle(jsonMappingProcessInfo);
        }
        return JacksonUtils.toJSONString(jsonNodes);
    }

    /**
     * 执行请求
     *
     * @param url        请求地址
     * @param reqJsonStr 请求json字符串
     * @return 请求结果
     * @throws IOException 异常
     */
    public String exec(String url, String reqJsonStr) throws IOException {
        //创建一个OkHttpClient对象
        OkHttpClient okHttpClient = new OkHttpClient();
        //创建一个RequestBody(参数1：数据类型 参数2传递的json串)
        RequestBody requestBody = RequestBody.create(reqJsonStr, MediaType.parse(org.springframework.http.MediaType.APPLICATION_JSON_VALUE));
        //创建一个请求对象
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        //发送请求获取响应
        Response response = okHttpClient.newCall(request).execute();
        assert response.body() != null;
        return Objects.requireNonNull(response.body()).string();
    }

    /**
     * 转化响应
     *
     * @param respMappingExpression 请求映射列表
     * @param respJsonStr          响应json
     */
    public String respTransform(MappingExpression respMappingExpression, String respJsonStr) throws JsonProcessingException {
        ObjectNode jsonNodes = JacksonUtils.parseObjectNode(respJsonStr);
        if (Objects.isNull(respMappingExpression)) {
            return JacksonUtils.toJSONString(jsonNodes);
        }
        List<JsonMappingSpec> fieldJsonList = Optional.ofNullable(respMappingExpression.getFieldJson()).orElse(Lists.newArrayList());
        for (JsonMappingSpec jsonMappingSpec : fieldJsonList) {
            // 访问者信息
            JsonMappingVisitInfo jsonMappingVisitInfo = new JsonMappingVisitInfo();
            jsonMappingVisitInfo.setName(jsonMappingSpec.getName());
            jsonMappingVisitInfo.setNode(jsonNodes);
            Visitor visitor = factory.getInstance(jsonMappingSpec.getType());
            // 执行访问者
            visitor.visit(jsonMappingVisitInfo);
            // 处理器信息
            JsonMappingProcessInfo jsonMappingProcessInfo = new JsonMappingProcessInfo();
            jsonMappingProcessInfo.setNodes(Optional.ofNullable(jsonMappingVisitInfo.getNodes()).orElse(Lists.newArrayList()));
            jsonMappingProcessInfo.setJsonMappingSpec(jsonMappingSpec);
            // 字段映射处理器
            jsonFieldProcessor.handle(jsonMappingProcessInfo);
        }
        return JacksonUtils.toJSONString(jsonNodes);
    }
}
